#include <gtest/gtest.h>
#include <AliOSSAdapter.h>
#include <SignatureGenerator.h>
#include <QDateTime>
#include <QCoreApplication>
#include <QUuid>
#include <map>
#include <xfunctional>
#include <string>
#include <AliOSSRemoveRequest.h>
#include <thread>
#include <conio.h>

int access(const QString &path)
{
    QNetworkAccessManager m;
    QNetworkRequest req(QString("http://tcres.oss-cn-shenzhen.aliyuncs.com/%1").arg(path));
    auto rep = m.get(req);
    while (!rep->isFinished())
        QCoreApplication::processEvents();
    rep->deleteLater();
    return rep->attribute(QNetworkRequest::Attribute::HttpStatusCodeAttribute).toInt();
}

using namespace AliOSS;

bool upload(const QString &path)
{
    // initialize
    // A user signature can only upload files
    AliOSS::SignatureGenerator userKeyGen = AliOSS::SignatureGenerator(
        "tcres",
        "sSot2oqZBG1xTcJs",
        "iwVz8BO5ewe1X1JC1CbMZTQNtdqQB6");

    AliOSSAdapter userAdapter("tcres", "sSot2oqZBG1xTcJs");

    // data to upload
    QByteArray data = "1234567";

    // prepare
    AliOSSUploadRequest *uploader = userAdapter.prepareUpload(data);

    // datetime string
    auto datetime = QDateTime::currentDateTime();
    datetime.setTime(QTime::currentTime().addSecs(-60 * 14));
    auto dtstr = AliOSSAdapter::formatDatetime(datetime.toUTC());

    // content Type
    QString contentType = "text/html";

    //ossheaders
    QMap<QString, QByteArray> ossHeaders = QMap<QString, QByteArray>({
        { "x-oss-meta-uploadtime", QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8() },
        { "x-oss-meta-uploader", "testUploader" },
    });

    // signature
    auto signature = userKeyGen.once("PUT", dtstr, path, uploader->fileMd5inBase64(), ossHeaders, contentType);

    // set properties
    uploader->setNeededInformation(dtstr, path, signature, "text/html", ossHeaders);

    // upload
    bool ok = false;
    bool failed = false;
    QObject::connect(uploader, &AliOSSUploadRequest::uploadFinished, [&]()
    {
        ok = true;
    });
    QObject::connect(uploader, &AliOSSUploadRequest::uploadFailed, [&]()
    {
        failed = true;
    });

    // auto-delete
    QObject::connect(uploader, &AliOSSUploadRequest::uploadFinished,
        uploader, &AliOSSUploadRequest::deleteLater);
    QObject::connect(uploader, &AliOSSUploadRequest::uploadFailed,
        uploader, &AliOSSUploadRequest::deleteLater);
    QObject::connect(uploader, &AliOSSUploadRequest::uploadError,
        uploader, &AliOSSUploadRequest::deleteLater);
    uploader->doUpload();

    while (!ok && !failed)
        QCoreApplication::processEvents();
    return ok && !failed;
}

bool copy(const QString &path, const QString &dstPath)
{
    bool ok = false;
    bool failed = false;

    AliOSS::SignatureGenerator genAdmin = AliOSS::SignatureGenerator(
        "tcres",
        "L9LT1rmDHa7P6HTF",
        "RC6cgjXoMiMHNjUwELKgYBEYdmnfnf");
    AliOSSAdapter adapterAdmin("tcres", "L9LT1rmDHa7P6HTF");


    AliOSSCopyRequest *copyRequest = adapterAdmin.prepareCopy(path, dstPath);
    QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFinished, [&]()
    {
        ok = true;
    });
    QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFailed, [&]()
    {
        failed = true;
    });

    // auto-delete
    QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFinished,
        copyRequest, &AliOSSCopyRequest::deleteLater);
    QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFailed,
        copyRequest, &AliOSSCopyRequest::deleteLater);
    QObject::connect(copyRequest, &AliOSSCopyRequest::uploadError,
        copyRequest, &AliOSSCopyRequest::deleteLater);

    auto dtstr = AliOSSAdapter::formatDatetime(QDateTime::currentDateTimeUtc());
    auto signature = genAdmin.once("PUT", dtstr, dstPath, QString(), copyRequest->ossHeaders());
    copyRequest->setNeededInformation(dtstr, signature);
    copyRequest->doCopy();

    while (!ok && !failed)
        QCoreApplication::processEvents();
    return ok && !failed;
}

bool remove(const QString &path)
{
    bool ok = false;
    bool failed = false;

    AliOSS::SignatureGenerator genAdmin = AliOSS::SignatureGenerator(
        "tcres",
        "L9LT1rmDHa7P6HTF",
        "RC6cgjXoMiMHNjUwELKgYBEYdmnfnf");
    AliOSSAdapter adapterAdmin("tcres", "L9LT1rmDHa7P6HTF");

    AliOSSRemoveRequest *removeRequest = adapterAdmin.prepareRemove(path);
    QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFinished, [&]()
    {
        ok = true;
    });
    QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFailed, [&]()
    {
        failed = true;
    });

    // auto-delete
    QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFinished,
        removeRequest, &AliOSSRemoveRequest::deleteLater);
    QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFailed,
        removeRequest, &AliOSSRemoveRequest::deleteLater);
    QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadError,
        removeRequest, &AliOSSRemoveRequest::deleteLater);

    auto dtstr = AliOSSAdapter::formatDatetime(QDateTime::currentDateTimeUtc());
    auto signature = genAdmin.once("DELETE", dtstr, path);
    removeRequest->setNeededInformation(dtstr, signature);
    removeRequest->doRemove();

    while (!ok && !failed)
        QCoreApplication::processEvents();
    return ok && !failed;
}

TEST(AliOSS, upload_copy_remove)
{
    // path
    QString path = "temp/test/testUpload_" + QUuid::createUuid().toString();

    // make sure it is not exist first
    ASSERT_EQ(404, access(path));

    // **upload first
    ASSERT_TRUE(upload(path));

    // check if uploaded
    ASSERT_EQ(200, access(path));

    // **copy it
    auto dstPath = path + ".copy";
    ASSERT_TRUE(copy(path, dstPath));

    // check if copied
    ASSERT_EQ(200, access(dstPath));
    ASSERT_EQ(200, access(path));

    // **remove both of them
    ASSERT_TRUE(remove(path));
    ASSERT_TRUE(remove(dstPath));

    // check if both removed
    ASSERT_EQ(404, access(dstPath));
    ASSERT_EQ(404, access(path));
}